// /components/HiglassWrapper.tsx
'use client';

import 'higlass/dist/hglib.css';
import { viewer } from 'higlass';
import React, { useEffect, useRef, useState } from 'react';
import { Spin, message } from 'antd';
import axiosInstanceForHiglass from '../utils/axiosForHiglass';
import { buildViewConfigFromUuids, Item } from '../utils/buildViewConfig';

interface Props {
    speciesNames: string; // "sp1,sp2,sp3"
    // keep these props for compatibility, but responsive behavior removed
    minViewWidth?: number;
    viewHeight?: number;
}

export default function HiglassWrapper({
    speciesNames,
    // minViewWidth not used now (kept for compatibility)
    minViewWidth = 420,
    viewHeight = 600
}: Props) {
    const containerRef = useRef<HTMLDivElement | null>(null);
    const hgvRef = useRef<any | null>(null);
    const [isLoading, setIsLoading] = useState(true);
    const itemsRef = useRef<Item[]>([]);
    // keep handlers so we can off() them on cleanup
    const handlersRef = useRef<Record<string, (loc: any) => void>>({});
    // programmaticSet used to avoid event feedback loops
    const programmaticSetRef = useRef<Set<string>>(new Set());

    // parse speciesNames -> ["a","b"]
    const parseSpecies = (s: string) =>
        (s || '')
            .split(',')
            .map(x => x.trim())
            .filter(Boolean);

    // fetch single uuid (reuses your existing endpoint behavior)
    const fetchUuidForSpecies = async (speciesName: string) => {
        const name = speciesName.replace('_', '');
        try {
            const res = await axiosInstanceForHiglass.get('uids_by_filename', {
                params: { d: name }
            });
            if (res?.data?.results?.length > 0) {
                return res.data.results[0].uuid as string;
            } else return null;
        } catch (err) {
            console.error('fetchUuidForSpecies error', err);
            return null;
        }
    };

    // build items (uuid pairs) and initialize higlass viewer
    useEffect(() => {
        let cancelled = false;
        const run = async () => {
            setIsLoading(true);
            const speciesArr = parseSpecies(speciesNames);
            if (!speciesArr.length) {
                message.error('No species provided.');
                setIsLoading(false);
                return;
            }

            // fetch uuids in parallel
            const uuids: any = await Promise.all(speciesArr.map(s => fetchUuidForSpecies(s)));

            const pairs: Item[] = [];
            for (let i = 0; i < speciesArr.length; i++) {
                if (uuids[i]) pairs.push({ species: speciesArr[i], uuid: uuids[i] });
                else message.warning(`mcool for "${speciesArr[i]}" not found, skipping`);
            }

            if (cancelled) return;
            if (!pairs.length) {
                message.error('No valid mcool files found.');
                setIsLoading(false);
                return;
            }
            itemsRef.current = pairs;

            // ensure container exists (we always render it)
            if (!containerRef.current) {
                setIsLoading(false);
                return;
            }

            // build initial viewConfig
            // <-- 不传 cols，buildViewConfigFromUuids 会用默认 cols (ceil(sqrt(n)))
            const { viewConfig, viewUids } = buildViewConfigFromUuids(pairs, {
                cols: speciesArr.length > 1 ? 2 : 1,
                viewHeight
            });
            // clear container first
            containerRef.current.innerHTML = '';

            try {
                // create viewer and keep ref
                const hgv = viewer(containerRef.current, viewConfig, {
                    bounded: true
                });
                hgvRef.current = hgv;

                // set up listeners for linking: for each viewUid, subscribe to 'location'
                viewUids.forEach((viewUid: string) => {
                    const handler = (location: any) => {
                        // ignore programmatic updates
                        if (programmaticSetRef.current.has(viewUid)) {
                            programmaticSetRef.current.delete(viewUid);
                            return;
                        }

                        const xDomain = location.xDomain ?? location.xRange;
                        const yDomain = location.yDomain ?? location.yRange;
                        if (!xDomain || !yDomain) return;

                        viewUids.forEach(tgt => {
                            if (tgt === viewUid) return;
                            programmaticSetRef.current.add(tgt);
                            try {
                                hgv.zoomTo(tgt, xDomain[0], xDomain[1], yDomain[0], yDomain[1], 0);
                            } catch (e) {
                                console.warn('zoomTo error', e);
                            } finally {
                                setTimeout(() => programmaticSetRef.current.delete(tgt), 150);
                            }
                        });
                    };

                    handlersRef.current[viewUid] = handler;
                    hgv.on && hgv.on('location', handler, viewUid);
                });
            } catch (err) {
                console.error('Higlass viewer init error:', err);
                message.error('Failed to initialize HiGlass viewer');
                hgvRef.current = null;
            } finally {
                if (!cancelled) setIsLoading(false);
            }
        };

        run();

        return () => {
            cancelled = true;
            // cleanup DOM/listeners if needed
            const hgv = hgvRef.current;
            if (hgv) {
                Object.entries(handlersRef.current).forEach(([viewUid, handler]) => {
                    try {
                        hgv.off && hgv.off('location', handler, viewUid);
                    } catch (e) {
                        // ignore
                    }
                });
            }
            if (containerRef.current) containerRef.current.innerHTML = '';
            hgvRef.current = null;
            handlersRef.current = {};
            programmaticSetRef.current.clear();
        };
    }, [speciesNames, viewHeight]);

    return (
        <div style={{ position: 'relative', width: '100%' }}>
            {/* container 始终存在，避免 ref 为 null */}
            <div
                ref={containerRef}
                style={{
                    width: '100%',
                    minHeight: Math.max(800, viewHeight),
                    maxHeight: '100vh',
                    // 加个 maxHeight 和 overflow 防止极端撑高（可选）
                    overflow: 'auto'
                }}
            />
            {/* loading overlay */}
            {isLoading && (
                <div
                    style={{
                        position: 'absolute',
                        inset: 0,
                        display: 'flex',
                        alignItems: 'center',
                        justifyContent: 'center',
                        background: 'rgba(255,255,255,0.6)',
                        zIndex: 10
                    }}
                >
                    <Spin size="large" />
                </div>
            )}
        </div>
    );
}
